﻿using POC.Configuration;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using wlog.Model;
using wlog.Shell;
using wlog.Shell.IO;

namespace wlog
{
    public class Program
    {        
        static string SOURCE = "Program";
        static string currentDirectory;
        static bool IsLocalDevelopment = false;
        public static string configFile = "azure.account.config";
        public static string nodeName = "/cset/azure/account";
        /// <summary>
        /// I wanted to be here, always, the entry point
        /// </summary>
        /// <param name="args"></param>
        public static void Main(string[] args)
        {
            // Lets configure it 
            bool InLineMode = InitilizeProgram(args);
            bool workInProgress = true;
            CommandController controller = new CommandController(IsLocalDevelopment);
            Queue<OutputChannel> outputs = new Queue<OutputChannel>();
            PrintCommand consolePrinter = new PrintCommand();
            while (workInProgress)
            { 
                string[] commands;
                if (InLineMode == true)
                {
                    // this means proper arguments from command line
                    commands = args;
                    Trace.TraceWarning("Argument greater than 3");
                }
                else
                {  
                    Trace.TraceInformation("Entering into command line");
                    // Get it from console
                    Console.Write(currentDirectory + "#>");
                    string commandargs = Console.ReadLine();
                    commands = GetArguments(commandargs);
                }
                // Parse it and create command
                ICommand command = controller.CreateCommand(commands);
                command.OnAddLine((string line) => { Console.WriteLine(line); });
                // Execute it
                if (command is CleanCommand)
                {
                    Trace.TraceWarning("Cleaning output " + command.GetType().FullName);                   
                    var result = command.Execute();
                    outputs.Clear();
                }
                else if (command is PrintCommand)
                {
                    var textPrinter = command as PrintCommand;
                    textPrinter.SetModels(outputs);
                    textPrinter.Execute();
                }
                else
                {
                    var result = command.Execute();
                    outputs.Enqueue(command.Results);
                    Trace.TraceWarning("Putting into buffer total = " + command.Results.Lines.Count());
                }
                // just execute once and exit the loop
                if (InLineMode == true || command is QuitCommand)
                {
                    Trace.Flush();
                    // This means we got good command from command console
                    workInProgress = false;
                }
            }
            // POC.Diagnostics.Logger.WriteEntry(SOURCE, "Ended Main at " + DateTime.Now, 10098);

        }
        /// <summary>
        /// This will be changed on LsCommand
        /// </summary>
        public static string CurrentDirectory
        {
            get { return Program.currentDirectory; }
            set { Program.currentDirectory = value; }
        }
        /// <summary>
        /// Lets configure the app as per the environment
        /// </summary>
        public static bool InitilizeProgram(string[] args)
        {
            // Expecting the config file to placed at current directory.. 
            ApplicationConfiguration.BindFileReader(configFile, nodeName);
            IsLocalDevelopment = ApplicationConfiguration.Get(ConfigKeyConstant.UseLocalAccount).ToBoolean();
            
            // POC.Diagnostics.Logger.WriteEntry(SOURCE, "Entering into Main at " + DateTime.Now, 10098);

            /* Uncomment when you want to see every exception details on the screen
            AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
            */

            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;

            /*
             * For Generating some logs here
            for (long l = 1000000; l < 1000040; l++)
            {
                POC.Diagnostics.Logger.WriteEntry(SOURCE, "Entering into Main at " + DateTime.Now, l);
                Thread.Sleep(10);
            }
             **/
            currentDirectory = Environment.CurrentDirectory;
            bool inlineMode = args.Length >= 3;

            //if (WebRequest.DefaultWebProxy.Credentials == null)
            //{
            //    WebRequest.DefaultWebProxy.Credentials = CredentialCache.DefaultCredentials;
            //}
            return inlineMode;
        }
        /// <summary>
        /// Could not fix the RegEx as of now.. So, Fixing the dot Commands
        /// </summary>
        /// <param name="lines"></param>
        /// <returns></returns>
        static string DotFix(string lines)
        {
            string convertedLine = lines.ToLower();
            // Let me build some alias
            if (convertedLine.ToLower() == "cd..")
            {
                convertedLine = "cd OneLevelUp"; // it is escaped in the command
            }
            else if (convertedLine == "cd\\")
            {
                convertedLine = "cd BackToRoot"; // it is escaped in the command
            }
            else
            {
                convertedLine = lines;
            }
            return convertedLine;
        }
        /// <summary>
        /// Let me have some arguments from user
        /// </summary>
        /// <param name="lines"></param>
        /// <returns></returns>
        static string[] GetArguments(string lines)
        {
            RegexOptions options = RegexOptions.None;
            Regex regex = new Regex(@"((""((?<token>.*?)(?<!\\)"")|(?<token>[\w]+))(\s)*)", options);
            var args = (from Match m in regex.Matches(DotFix(lines))
                        where m.Groups["token"].Success
                        select m.Groups["token"].Value).ToList();
            // args.ForEach(x => Console.WriteLine(x));
            return args.ToArray();
        }
        /// <summary>
        /// It is a must for async logging...
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            POC.Diagnostics.Logger.Flush();
        }
        /// <summary>
        /// This is optionals..
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
        {
            ReportException(e.Exception);
        }
        /// <summary>
        /// Always override this to proper channel, for me the current channel is console
        /// </summary>
        /// <param name="ex"></param>
        public static void ReportException(Exception ex)
        {
            string errors = "Error on application " + ex.ToString();
            Console.WriteLine(errors);
        }
    }
}
